﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Web;

namespace ThreadEyePro
{
    public partial class FormMain : Form
    {
        KaskusConnection kaskus;
        Timer timertick;
        BackgroundWorker worker;
        FormBrowser kaskusBrowser = null;
        FormOptions opts = null;
        bool initial = true;
        int newPostCount = 0;

        string optUseBrowser = "Built-in Browser";
        int optTimer = 1;
        bool optEnableSound = false;
        string optSoundFile = string.Empty;
        bool optEnablePM = false;

        public FormMain()
        {
            InitializeComponent();
            kaskus = new KaskusConnection();

            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
            worker.WorkerReportsProgress = true;
            worker.WorkerSupportsCancellation = true;

            notifyIcon1.Click += new EventHandler(notifyIcon1_Click);
            notifyIcon1.BalloonTipClicked += new EventHandler(notifyIcon1_BalloonTipClicked);
            notifyIcon1.BalloonTipShown += new EventHandler(notifyIcon1_BalloonTipShown);

            gridView.CellDoubleClick += new DataGridViewCellEventHandler(gridView_CellDoubleClick);
            gridView.CellValueChanged += new DataGridViewCellEventHandler(gridView_CellValueChanged);

            this.Resize += new EventHandler(FormMain_Resize);
            this.Disposed += new EventHandler(FormMain_Disposed);

            buttonPm.Visible = false;
        }

        void startWork()
        {
            if (!worker.IsBusy)
            {
                labelSignedIn.Text = "Updating...";
                worker.RunWorkerAsync(new KaskusJob(kaskus, gridView));
            }
        }

        void goToKaskus(string url)
        {
            if (optUseBrowser == "System Default")
            {
                System.Diagnostics.Process.Start(url);
            }
            else if (optUseBrowser == "Mozilla Firefox")
            {
                System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo("C:\\Program Files\\Mozilla Firefox\\firefox.exe", url);
                System.Diagnostics.Process.Start(ps);
            }
            else
            {
                if (kaskusBrowser == null)
                {
                    kaskusBrowser = new FormBrowser();
                }
                kaskusBrowser.Show();
                kaskusBrowser.TopMost = true;
                kaskusBrowser.WindowState = FormWindowState.Normal;
                kaskusBrowser.TopMost = false;
                kaskusBrowser.Focus();
                kaskusBrowser.navigateTo(url);                
            }
        }

        private void buttonOptions_Click(object sender, EventArgs e)
        {
            if (opts == null)
            {
                opts = new FormOptions();
            }
            opts.optBrowser = this.optUseBrowser;
            opts.optSound = this.optEnableSound;
            opts.optTimer = this.optTimer;
            opts.optSoundFile = this.optSoundFile;
            opts.optEnablePM = this.optEnablePM;

            DialogResult opt = opts.ShowDialog();
            if (opt == DialogResult.OK)
            {
                this.optUseBrowser = opts.optBrowser;
                this.optEnableSound = opts.optSound;
                this.optTimer = opts.optTimer;
                this.optSoundFile = opts.optSoundFile;

                this.optEnablePM = opts.optEnablePM;
                if (this.optEnablePM)
                {
                    kaskus.username = opts.kaskusUsername;
                    kaskus.password = opts.kaskusPassword;
                    buttonPm.Visible = true;
                    buttonPm.Enabled = false;
                }
                else
                {
                    kaskus.username = string.Empty;
                    kaskus.password = string.Empty;
                    buttonPm.Visible = false;
                }
            }
            this.labelInfo.Text = "Timer: " + optTimer.ToString() + " minutes";
        }

        void addToGrid(int threadId)
        {// grid: hash, threadId, title, pageNo, postCount, status
            KaskusThreadObject thread = new KaskusThreadObject(threadId);
            thread.pageNo = 1;
            thread.postCount = 0;
            gridView.Rows.Add(new string[] { thread.threadHash, threadId.ToString(), "Thread " + threadId.ToString(), "1", "0", "-" });
        }

        void playSoundAlert()
        {
            string file = optSoundFile;
            if (file == string.Empty || !File.Exists(file))
            {
                return;
            }
            using (var player = new System.Media.SoundPlayer(file))
            {
                player.Play();
            }
        }

        void enableAllButton(bool enable)
        {
            buttonAdd.Enabled = enable;
            buttonRemove.Enabled = enable;
            buttonStart.Enabled = enable;
            buttonStart.Enabled = enable;
            buttonPm.Enabled = enable;
            buttonOptions.Enabled = enable;
        }

        void dataSerialize(object o, string filename)
        {
            filename = Application.StartupPath + @"\" + filename;
            using (var ms = File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                formatter.Serialize(ms, o);
                ms.Flush();
                ms.Close();
                ms.Dispose();
                formatter = null;
            }
        }

        object dataDeserialize(string filename)
        {
            object o = null;
            filename = Application.StartupPath + @"\" + filename;
            if (File.Exists(filename))
            {
                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                using (var fStream = File.Open(filename, FileMode.Open))
                {
                    o = formatter.Deserialize(fStream);
                    fStream.Close();
                }
                formatter = null;
            }
            return o;
        }

        void gridSerialize()
        {// grid: hash, threadId, title, pageNo, postCount, status
            ArrayList threadList = new ArrayList();
            if (gridView.Rows.Count >= 1)
            {
                foreach (DataGridViewRow r in gridView.Rows)
                {
                    threadList.Add(r.Cells[0].Value.ToString());
                }
            }
            dataSerialize(threadList, "threads.dat");
        }

        void gridDeserialize()
        {
            ArrayList threadList = (ArrayList)dataDeserialize("threads.dat");
            foreach (string t in threadList)
            {// grid: hash, threadId, title, pageNo, postCount, status         
                KaskusThreadObject thread = new KaskusThreadObject(t);
                thread.postCount = 0;
                gridView.Rows.Add(new string[] { thread.threadHash, threadId.ToString(), "Thread " + thread.threadId.ToString(), "-", "-", "-" });
            }
        }

        void timertick_Tick(object sender, EventArgs e)
        {
            startWork();
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {// grid: hash, threadId, title, pageNo, postCount, status
            KaskusJob job = (KaskusJob)e.Argument;

            for (int i = 0; i < job.gridview.Rows.Count; i++)
            {
                string currentHash = job.gridview.Rows[i].Cells[0].Value.ToString();
                string currentStatus = job.gridview.Rows[i].Cells[5].Value.ToString();
                if (currentStatus == "Invalid Thread")
                {
                    continue;
                }

                job.thread = new KaskusThreadObject(currentHash);

                if (job.thread.postCount < 1)
                {
                    job.thread.status = "Monitoring";
                }

                KaskusThreadObject t = job.connection.requestThread(job.thread);
                if (job.connection.lastError != string.Empty)
                {
                    job.thread.status = "Connection Error";
                    job.thread.index = i;
                    worker.ReportProgress(i, job.thread);
                    break;
                }
                else if (t.postCount < 1)
                {
                    job.thread.postCount = 0;
                    job.thread.status = "Invalid Thread";
                    job.thread.index = i;
                    worker.ReportProgress(i, job.thread);
                    continue;
                }
                else
                {
                    job.thread = t;
                }

                if (job.thread.status == string.Empty)
                {
                    if (currentHash != job.thread.threadHash)
                    {
                        job.thread.status = "New Post";
                    }
                    else
                    {
                        job.thread.status = "No New";
                    }
                }
                else if (job.thread.status == "Monitoring")
                {
                    t = job.connection.requestThread(job.thread);
                    if (job.connection.lastError != string.Empty)
                    {
                        job.thread.status = "Connection Error";
                        job.thread.index = i;
                        worker.ReportProgress(i, job.thread);
                        break;
                    }
                    else if (t.postCount < 1)
                    {
                        job.thread.postCount = 0;
                        job.thread.status = "Invalid Thread";
                        job.thread.index = i;
                        worker.ReportProgress(i, job.thread);
                        continue;
                    }
                    else
                    {
                        job.thread = t;
                    }
                }

                job.thread.index = i;
                worker.ReportProgress(i, job.thread);
            }
            e.Result = job;
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {// grid: hash, threadId, title, pageNo, postCount, status
            KaskusThreadObject t = (KaskusThreadObject)e.UserState;
            if (t.status != "Connection Error")
            {
                if (t.status == "Invalid Thread")
                {
                    t.threadTitle = "-- Invalid Thread --";
                }

                gridView.Rows[t.index].Cells[0].Value = t.threadHash;
                gridView.Rows[t.index].Cells[2].Value = t.threadTitle;
                gridView.Rows[t.index].Cells[3].Value = t.realPageNo;
                gridView.Rows[t.index].Cells[4].Value = t.totalPostCount;
                gridView.Rows[t.index].Cells[5].Value = t.status;

                if (t.status == "New Post")
                {
                    notifyIcon1.BalloonTipTitle = t.threadTitleSafe + "...";
                    notifyIcon1.BalloonTipText = "This thread has new posts!";
                    newPostCount++;
                }
            }

        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            KaskusJob job = (KaskusJob)e.Result;
            kaskus = job.connection;

            if (kaskus.lastError != string.Empty)
            {
                notifyIcon1.BalloonTipTitle = "Cannot connect to Kaskus!";
                notifyIcon1.BalloonTipText = "Please check Kaskus or either your internet is down?";
                notifyIcon1.ShowBalloonTip(8000);
                labelSignedIn.Text = "Cannot connect to Kaskus!";
                kaskus.lastError = string.Empty;
                buttonStart.Enabled = true;
                return;
            }

            if (initial)
            {
                initial = false;
                int minute = optTimer;
                timertick = new Timer();
                timertick.Interval = (minute * 1000) * 60;
                timertick.Tick += new EventHandler(timertick_Tick);
                timertick.Start();
                buttonStart.Enabled = true;
            }
            
            if (kaskus.isLoggedIn)
            {
                //labelSignedIn.Text = kaskus.username + " is online!";
                buttonPm.Text = kaskus.pm;
            }
            else
            {
                //labelSignedIn.Text = "Not logged in.";
                buttonPm.Text = "PM";
            }

            if (newPostCount >= 1)
            {
                labelSignedIn.Text = "New post on " + newPostCount.ToString() + " subscribed threads.";
                notifyIcon1.ShowBalloonTip(8000);
                newPostCount = 0;
            }
            else
            {
                labelSignedIn.Text = "No new post";
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            if (buttonStart.Text == "STOP")
            {
                timertick.Stop();
                enableAllButton(true);
                buttonStart.Text = "START";
                labelSignedIn.Text = "Stopped.";
            }
            else
            {
                if (gridView.Rows.Count < 1)
                {
                    MessageBox.Show("Please add a thread first!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }
                labelSignedIn.Text = "Connecting...";
                enableAllButton(false);
                buttonStart.Text = "STOP";
                initial = true;
                startWork();
            }
        }

        private void buttonAbout_Click(object sender, EventArgs e)
        {
            string about = "";
            about += "Thread Eye" + Environment.NewLine;
            about += Environment.NewLine;
            about += "Allows you subscribe KASKUS forum, and get notified for updates." + Environment.NewLine;
            about += "This program is FREE, and intended for educational purpose only." + Environment.NewLine;
            about += "Please use at your own risk" + Environment.NewLine;
            about += Environment.NewLine;
            about += "Developer: betzeika <betzeika@gmail.com>";
            about += Environment.NewLine;
            about += "Using Microsoft .NET / C# ";

            MessageBox.Show(about, "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void buttonAdd_Click(object sender, EventArgs e)
        {
            FormAddThread a = new FormAddThread();
            DialogResult r = a.ShowDialog();
            if (r == DialogResult.OK)
            {
                addToGrid(a.threadId);
            }
        }

        private void buttonRemove_Click(object sender, EventArgs e)
        {
            if (gridView.Rows.Count < 1)
            {
                return;
            }
            foreach (DataGridViewRow r in gridView.SelectedRows)
            {
                gridView.Rows.Remove(r);
            }
        }

        void gridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            KaskusThreadObject thread = new KaskusThreadObject(gridView.Rows[e.RowIndex].Cells[0].Value.ToString());
            goToKaskus(thread.threadUrl);
        }

        void gridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            int index = e.RowIndex;
            Font f;
            //DataGridViewTriState w;
            string status = gridView.Rows[e.RowIndex].Cells[5].Value.ToString();
            if (status == "New Post" || status == "No New")
            {
                if (status == "New Post")
                {
                    f = new Font(gridView.Font, FontStyle.Bold);
                    //w = DataGridViewTriState.True;
                }
                else
                {
                    f = new Font(gridView.Font, FontStyle.Regular);
                    //w = DataGridViewTriState.NotSet;
                }

                foreach (DataGridViewCell cell in gridView.Rows[e.RowIndex].Cells)
                {
                    cell.Style.Font = f;
                    // cell.Style.WrapMode = w;
                }
            }
            else if (status == "Invalid Thread")
            {
                gridView.Rows[e.RowIndex].Cells[2].Style.Font = new Font(gridView.Font, FontStyle.Italic);
                gridView.Rows[e.RowIndex].Cells[2].Style.ForeColor = Color.Red;
            }
        }

        void notifyIcon1_BalloonTipShown(object sender, EventArgs e)
        {
            if (optEnableSound)
            {
                playSoundAlert();
            }
        }

        void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
        {
            this.Show();
            this.ShowInTaskbar = true;
            this.TopMost = true;
            this.WindowState = FormWindowState.Normal;
            this.TopMost = false;
            this.Focus();
        }

        void notifyIcon1_Click(object sender, EventArgs e)
        {
            this.Show();
            this.ShowInTaskbar = true;
            this.TopMost = true;
            this.WindowState = FormWindowState.Normal;
            this.TopMost = false;
            this.Focus();
        }

        void FormMain_Disposed(object sender, EventArgs e)
        {
            this.notifyIcon1.Dispose();
        }

        void FormMain_Resize(object sender, EventArgs e)
        {
            this.ShowInTaskbar = !(this.WindowState == FormWindowState.Minimized);
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            labelSignedIn.Text = "";
            try
            {
                // Load data
                gridDeserialize();
                kaskus.cookies = (CookieContainer)dataDeserialize("cookies.dat");
            }
            catch (Exception ex) 
            {
                if (File.Exists(Application.StartupPath + @"\cookies.dat") || File.Exists(Application.StartupPath + @"\threads.dat"))
                {
                    MessageBox.Show("Cannot continue last saved session, data corrupted!" + Environment.NewLine + "Please delete *.dat file from the program startup directory", "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            if (worker.IsBusy)
            {
                MessageBox.Show("Please wait the process finish before closing program.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                e.Cancel = true;
                base.OnClosing(e);
                return;
            }
            DialogResult r = MessageBox.Show("Are you sure want to quit this program?", "Thread Eye Pro", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (r == DialogResult.Yes)
            {
                try
                {
                    // Serialize data...
                    gridSerialize();
                    dataSerialize(kaskus.cookies, "cookies.dat");
                }
                catch (Exception ex) 
                {
                    MessageBox.Show("Cannot save session!" + Environment.NewLine + "Make sure this program's directory is writeable!", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                if (kaskusBrowser != null)
                {
                    kaskusBrowser.Close();
                    kaskusBrowser.Dispose();
                }
                base.OnClosing(e);
                this.Dispose();
                this.Close();
            }
            else
            {
                e.Cancel = true;
                base.OnClosing(e);
            }
        }

    }
}
